package rolesystem.agentlogic.managers;

import java.util.ArrayList;

import rolesystem.agentlogic.parameters.ConfigurationParameters;
import rolesystem.agentlogic.utilityclasses.CarrierData;
import rolesystem.core.RoleException;
import rolesystem.core.RoleRegistration;
import rolesystem.core.RoleSystem;
import rolesystem.middle.managers.DataOutputManager;
import rolesystem.roles.carry.Manager;
import rolesystem.middle.managers.EnvironmentManager;
import rolesystem.middle.utilityclasses.PositionSq;
import rolesystem.middle.utilityclasses.SearchResult;
import rolesystem.middle.utilityclasses.Utils;

public class MovementManager 
{	
	/**
	 * The identifier of the agent
	 */
	private int id;
	
	/**
	 * Actual position of the agent
	 */
	private PositionSq position;
		
	/**
	 * Coordinates of the Agent's Base - The position in witch the agent will carry and unload an object
	 */
	private PositionSq base;
		
	/**
	 * The position in witch the agent will start
	 */
	private PositionSq start_position;
	
	/**
	 * Manage the contact (with other agents) phase 
	 */
	private ContactManager contactManager;
	

	public MovementManager(int id, int start_x, int start_y,int base_x, int base_y,ContactManager contactManager)
	{
		this.id=id;
		this.start_position=new PositionSq(start_x,start_y);
		this.position=new PositionSq(start_x,start_y);
		this.base=new PositionSq(base_x,base_y);
		this.contactManager=contactManager;
	}
	
	public void reset()
	{
		this.position=new PositionSq(start_position.x,start_position.y);
		
		EnvironmentManager.updateAgentPosition(id,position);
	}
	
	public void reset(PositionSq new_start)
	{
		this.start_position=new PositionSq(new_start);
		this.position=new PositionSq(start_position);
		
		EnvironmentManager.updateAgentPosition(id,position);
	}
	
	/**
	 * Put the agent to sleep for a number of milliseconds. 
	 * 
	 * @param pAUSE2 Number of ms that the agent will sleep 
	 * 
	 */	
	private void sleepAgent(long pAUSE2) 
	{	
		try
		{
			Thread.sleep(pAUSE2);
		} catch(InterruptedException ie) {}
	}
	
	
	public void setPosition(PositionSq position)
	{
		this.position=position;
		DataOutputManager.dataSim.getAgentData(id).incnumberofMovements();
		drawVisualization();
	}
	
	public PositionSq getPosition()
	{
		return new PositionSq(this.position);
	}
	
	
	public ArrayList<SearchResult> searchObject()
	{
		return EnvironmentManager.searchObjectInRadius(position,10,ConfigurationParameters.contactType==3);
	}
	
	private boolean reachObjectStep(PositionSq goal, boolean group, RoleSystem roleSystem, RoleRegistration registration, ArrayList<CarrierData> risposte_carrier, boolean sameSquare, boolean carry) throws RoleException
	{
		while(true)
		{
			sleepAgent(ConfigurationParameters.PAUSE);
			
			PositionSq dir= Utils.calcDirection(position, goal);
				
			if(dir.isZero())
			{	
				if(group)
				{	
					for(int i=0;i<risposte_carrier.size();i++)
						registration.doAction(Manager.carryFinished(risposte_carrier.get(i).getAgentID()));
				}
				break;
			}
			
			if(!group && !carry)
			{
				if(contactManager.checkMovementInterrupt(registration)) // If i received a help request, communicate and reach the sender 
					return true;
			}	
			
			boolean sameSquareOK=(Math.abs(PositionSq.diff(goal, position).getX())<=1 && Math.abs(PositionSq.diff(goal, position).getY())<=1 && sameSquare);
			
			if(EnvironmentManager.getObstacle(PositionSq.add(position, dir))==true || (EnvironmentManager.getAgentHere(PositionSq.add(position, dir)) && (sameSquareOK==false)))
				position.set(EnvironmentManager.getAlternativeRoute(position,dir));			
			else
				position.add(dir);
			
			DataOutputManager.dataSim.getAgentData(id).incnumberofMovements();
			
			if(group)
				contactManager.collaborativeCarryMessages(registration, risposte_carrier,position);
				
			drawVisualization();
		}
		
		drawVisualization();
		
		return false;
	}
	
	
	public boolean reachObject(PositionSq goal,RoleRegistration registration) throws RoleException
	{
		DataOutputManager.dataSim.getAgentData(id).setMode(ConfigurationParameters.MODE_MOVEMENT_INT);
		
		boolean earlyEnd=reachObjectStep(goal,false ,null,registration, null,true, false);
		
		DataOutputManager.dataSim.getAgentData(id).setMode(ConfigurationParameters.MODE_NULL);
		
		DataOutputManager.printString(id +" Object Reached");
		
		return earlyEnd;
	}
	
	public void reachObjectNoInt(PositionSq goal) throws RoleException
	{
		DataOutputManager.dataSim.getAgentData(id).setMode(ConfigurationParameters.MODE_MOVEMENT_NO_INT);
		
		reachObjectStep(goal,false ,null,null, null,true, true);
		
		//DataOutputManager.printString(id +" Object Reached");
		
		DataOutputManager.dataSim.getAgentData(id).setMode(ConfigurationParameters.MODE_NULL);
	}
	
	public void carryObjectSimple() throws RoleException
	{
		DataOutputManager.dataSim.getAgentData(id).setMode(ConfigurationParameters.MODE_MOVEMENT_NO_INT);
		
		DataOutputManager.printString(id +" Object Trasport Start");
		
		reachObjectStep(base, false , null, null, null,ConfigurationParameters.MULTIPLE_AGENTS_NEST, true);
		
		DataOutputManager.printString(id +" Base Reached: unloading object"); 		// Ho raggiunto la base
		
		DataOutputManager.dataSim.getAgentData(id).setMode(ConfigurationParameters.MODE_NULL);
	}
	
	public void carryObjectGroup(RoleSystem roleSystem, RoleRegistration registration, ArrayList<CarrierData> risposte_carrier) throws RoleException
	{	
		DataOutputManager.dataSim.getAgentData(id).setMode(ConfigurationParameters.MODE_MOVEMENT_NO_INT);
		
		reachObjectStep(base,true,roleSystem,registration,risposte_carrier,ConfigurationParameters.MULTIPLE_AGENTS_NEST,true);
		
		DataOutputManager.printString(id +" Base Reached: unloading object"); 	// Ho raggiunto la base
		DataOutputManager.printString(id +" Collaborative trasport concluded");
		
		DataOutputManager.dataSim.getAgentData(id).setMode(ConfigurationParameters.MODE_NULL);
	}
	
	public void randomMovement(RoleRegistration registration) throws RoleException
	{
		DataOutputManager.dataSim.getAgentData(id).setMode(ConfigurationParameters.MODE_MOVEMENT_INT);
		
		//DataOutputManager.printString(id +" Search concluded. NO OBJECT FOUND.Random movement");
			
		PositionSq new_position;
		do
		{
			new_position= new PositionSq(((int) (Math.random()*20))-10+position.x, 
										 ((int) (Math.random()*20))-10+position.y);
		
			new_position.checkAndCorrectBounds();
		}while(EnvironmentManager.getObstacleAndAgents(new_position));	
									
		reachObjectStep(new_position,false ,null,registration, null,false,false);
						
		//DataOutputManager.printString(id +" Random Momement end. X: " + position.x + " Y: " + position.y);
		
		DataOutputManager.dataSim.getAgentData(id).setMode(ConfigurationParameters.MODE_NULL);
	}
	
	public void drawVisualization()
	{	
		EnvironmentManager.updateAgentPosition(id,position);
		EnvironmentManager.updateVisualization();	
	}
	
}
